﻿using System;
using System.Linq;
using System.Runtime.InteropServices;

namespace Btstack
{
    public partial class Bluetooth
    {
        [StructLayout(LayoutKind.Sequential)]
        public readonly struct GattClientServiceType
        {
            public readonly ushort StartGroupHandle;
            public readonly ushort EndGroupHandle;
            public readonly ushort uuid16;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
            public readonly byte[] uuid128;
        }

        [StructLayout(LayoutKind.Sequential)]
        public readonly struct GattClientCharacteristicType
        {
            public readonly ushort StartHandle;
            public readonly ushort ValueHandle;
            public readonly ushort EndHandle;
            public readonly ushort properties;
            public readonly ushort uuid16;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
            public readonly byte[] uuid128;
        }

        [StructLayout(LayoutKind.Sequential)]
        public readonly struct GattClientNotificationType
        {
            private readonly BtstackLinkedItemType item;
            private readonly BtstackPacketHandlerType callback;
            private readonly ushort ConHandle;
            private readonly ushort AttributeHandle;
        }

        [DllImport("btstack-c.dll", EntryPoint = "gatt_client_init")]
        private static extern void GattClientInit();

        [DllImport("btstack-c.dll", EntryPoint = "gatt_client_discover_primary_services")]
        private static extern byte GattClientDiscoverPrimaryServices(BtstackPacketHandlerType callback, ushort ConHandle);

        [DllImport("btstack-c.dll", EntryPoint = "gatt_client_discover_primary_services_by_uuid16")]
        private static extern byte GattClientDiscoverPrimaryServicesByUuid16(BtstackPacketHandlerType callback, ushort ConHandle, ushort uuid16);

        [DllImport("btstack-c.dll", EntryPoint = "gatt_client_discover_characteristics_for_service")]
        private static extern byte GattClientDiscoverCharacteristicsForService(BtstackPacketHandlerType callback, ushort ConHandle, IntPtr service);

        [DllImport("btstack-c.dll", EntryPoint = "gatt_client_discover_characteristics_for_service_by_uuid16")]
        private static extern byte GattClientDiscoverCharacteristicsForServiceByUuid16(BtstackPacketHandlerType callback, ushort ConHandle, IntPtr service, ushort uuid16);

        [DllImport("btstack-c.dll", EntryPoint = "gatt_client_deserialize_service")]
        private static extern void GattClientDeserializeService(IntPtr packet, int offset, IntPtr service);

        [DllImport("btstack-c.dll", EntryPoint = "gatt_client_deserialize_characteristic")]
        private static extern byte GattClientDeserializeCharacteristic(IntPtr packet, int offset, IntPtr characteristic);

        [DllImport("btstack-c.dll", EntryPoint = "gatt_client_listen_for_characteristic_value_updates")]
        private static extern void GattClientListenForCharacteristicValueUpdates(IntPtr notification, BtstackPacketHandlerType PacketHandler, ushort ConHandle, IntPtr characteristic);

        [DllImport("btstack-c.dll", EntryPoint = "gatt_client_write_client_characteristic_configuration")]
        private static extern byte GattClientWriteClientCharacteristicConfiguration(BtstackPacketHandlerType callback, ushort ConHandle, IntPtr characteristic, ushort configuration);

        [DllImport("btstack-c.dll", EntryPoint = "gatt_client_stop_listening_for_characteristic_value_updates")]
        private static extern void GattClientStopListeningForCharacteristicValueUpdates(IntPtr notification);

        [DllImport("btstack-c.dll", EntryPoint = "gatt_client_write_value_of_characteristic_without_response")]
        private static extern byte GattClientWriteValueOfCharacteristicWithoutResponse(ushort ConHandle, ushort ValueHandle, ushort ValueLength, IntPtr value);

        private static void GattEventServiceQueryResultGetService(IntPtr packet, out GattClientServiceType service)
        {
            IntPtr ServicePtr = Marshal.AllocHGlobal(Marshal.SizeOf<GattClientServiceType>());

            GattClientDeserializeService(packet, 4, ServicePtr);

            service = Marshal.PtrToStructure<GattClientServiceType>(ServicePtr);

            Marshal.FreeHGlobal(ServicePtr);
        }

        private static void GattEventCharacteristicQueryResultGetCharacteristic(IntPtr packet, out GattClientCharacteristicType characteristic)
        {
            IntPtr characteristicPtr = Marshal.AllocHGlobal(Marshal.SizeOf<GattClientCharacteristicType>());

            GattClientDeserializeCharacteristic(packet, 4, characteristicPtr);

            characteristic = Marshal.PtrToStructure<GattClientCharacteristicType>(characteristicPtr);

            Marshal.FreeHGlobal(characteristicPtr);
        }

        private static byte GattClientDiscoverCharacteristicsForServiceByUuid16(BtstackPacketHandlerType callback, ushort ConHandle, GattClientServiceType service, ushort uuid16)
        {
            IntPtr ServicePtr = Marshal.AllocHGlobal(Marshal.SizeOf<GattClientServiceType>());
            Marshal.StructureToPtr(service, ServicePtr, false);

            byte ret = GattClientDiscoverCharacteristicsForServiceByUuid16(callback, ConHandle, ServicePtr, uuid16);

            Marshal.FreeHGlobal(ServicePtr);

            return ret;
        }

        private static byte GattClientDiscoverCharacteristicsForService(BtstackPacketHandlerType callback, ushort ConHandle, GattClientServiceType service)
        {
            IntPtr ServicePtr = Marshal.AllocHGlobal(Marshal.SizeOf<GattClientServiceType>());
            Marshal.StructureToPtr(service, ServicePtr, false);

            byte ret = GattClientDiscoverCharacteristicsForService(callback, ConHandle, ServicePtr);

            Marshal.FreeHGlobal(ServicePtr);

            return ret;
        }

        private static byte GattEventQueryCompleteGetAttStatus(byte[] packet)
        {
            return packet[4];
        }

        public static byte[] GattEventNotificationGetValue(byte[] packet)
        {
            int length = BitConverter.ToUInt16(packet, 6);
            return packet.Skip(8).Take(length).ToArray();
        }

        public static ushort GattEventNotificationGetValueHandle(byte[] packet)
        {
            return BitConverter.ToUInt16(packet, 4);
        }
    }
}
